package cool.doudou.doudada.data.audit.core.helper;

import lombok.extern.slf4j.Slf4j;
import org.javers.core.Changes;
import org.javers.core.Javers;
import org.javers.core.JaversBuilder;
import org.javers.core.diff.Change;
import org.javers.core.diff.Diff;
import org.javers.core.diff.changetype.NewObject;
import org.javers.core.diff.changetype.ObjectRemoved;
import org.javers.core.diff.changetype.ValueChange;
import org.javers.core.diff.custom.CustomValueComparator;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

import static org.javers.core.diff.ListCompareAlgorithm.LEVENSHTEIN_DISTANCE;

/**
 * DataAuditHelper
 *
 * @author jiangcs
 * @since 2022/10/18
 */
@Slf4j
public class DataAuditHelper {
    /**
     * 自定义 比较器
     */
    private final Map<Class, CustomValueComparator> comparatorMap = new HashMap<>() {{
        // BigDecimal类型
        put(BigDecimal.class, new CustomValueComparator<BigDecimal>() {
            @Override
            public boolean equals(BigDecimal a, BigDecimal b) {
                return a.compareTo(b) == 0;
            }

            @Override
            public String toString(BigDecimal value) {
                return value.toString();
            }
        });
        // Date类型
        put(Date.class, new CustomValueComparator<Date>() {
            @Override
            public boolean equals(Date a, Date b) {
                return a.getTime() == b.getTime();
            }

            @Override
            public String toString(Date value) {
                return value.toInstant().toString();
            }
        });
    }};

    /**
     * 比较
     *
     * @param srcObj 源对象
     * @param desObj 目标对象
     * @param <T>    对象类
     * @return true-存在差异；false-不存在差异
     */
    public <T> boolean compare(T srcObj, T desObj) {
        Javers javers = init();
        Diff diff = javers.compare(srcObj, desObj);
        log.info(diff.prettyPrint());

        return diff.hasChanges();
    }

    /**
     * 比较
     *
     * @param srcObj        源对象
     * @param desObj        目标对象
     * @param addConsume    新增消费回调
     * @param deleteConsume 删除消费回调
     * @param updateConsume 修改消费回调
     * @param <T>           对象类
     */
    public <T> void compare(T srcObj, T desObj, Consumer<T> addConsume, Consumer<T> deleteConsume, Consumer<T> updateConsume) {
        Javers javers = init();
        Diff diff = javers.compare(srcObj, desObj);
        log.info(diff.prettyPrint());

        Changes changes = diff.getChanges();
        for (Change change : changes) {
            if ((change instanceof NewObject && Objects.nonNull(addConsume))) {
                change.getAffectedObject().ifPresent(item -> addConsume.accept((T) item));
            }
            if ((change instanceof ObjectRemoved && Objects.nonNull(deleteConsume))) {
                change.getAffectedObject().ifPresent(item -> deleteConsume.accept((T) item));
            }
            if ((change instanceof ValueChange && Objects.nonNull(updateConsume))) {
                change.getAffectedObject().ifPresent(item -> updateConsume.accept((T) item));
            }
        }
    }

    /**
     * 添加 自定义比较器
     *
     * @param clazz                 Class类
     * @param customValueComparator 比较器
     * @param <T>                   对象类
     */
    public <T> void addComparator(Class<T> clazz, CustomValueComparator<T> customValueComparator) {
        comparatorMap.put(clazz, customValueComparator);
    }

    /**
     * 初始化
     *
     * @return Javers实例
     */
    private Javers init() {
        JaversBuilder javersBuilder = JaversBuilder.javers().withListCompareAlgorithm(LEVENSHTEIN_DISTANCE);

        // 自定义比较器
        comparatorMap.keySet().forEach(type -> javersBuilder.registerValue(type, comparatorMap.get(type)));

        return javersBuilder.build();
    }
}
